﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using HomeFramework.Core;

namespace TNX.Common.Legacy
{
    /// <summary>
    /// AppDomain utility for plugin execution on another appdomain
    /// </summary>
    public class AppDomainContextSmart<RQ, RE> : IDisposable
        where RQ : struct
        where RE : struct
    {
        protected readonly AppDomain Domain;

        public AppDomainContextSmart()
        {
            try
            {
                Domain = AppDomain.CreateDomain(string.Format("AppDomainFunc_{0}", Guid.NewGuid()), AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
            }
            catch (Exception ex)
            {
                Log.Debug("Unable to create a new AppDomainFunc: {0}", ex.Message);
                throw;
            }

            if (typeof(RQ).GetCustomAttributes(typeof(SerializableAttribute), true).Count() != 1)
                throw new ArgumentException("Generic RQ must support SerializableAttribute attribute!");

            if (typeof(RE).GetCustomAttributes(typeof(SerializableAttribute), true).Count() != 1)
                throw new ArgumentException("Generic RE must support SerializableAttribute attribute!");
        }

        /// <summary>
        /// Process the request on cross appdomain
        /// Inner handler must not use variables in scope other then with in Request parameter
        /// </summary>
        public virtual RE ExecuteCrossDomainOperation(Func<RQ, RE> handler, RQ request)
        {
            using (var domainHandler = (CrossDomainHandler)Domain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(CrossDomainHandler).FullName))
            {
                bool result;
                Exception ex;

                var risp = domainHandler.Execute(handler, request, out result, out ex);

                if (result)
                    return risp;
                else
                    throw ex;
            }
        }

        protected class CrossDomainHandler : MarshalByRefObject, IDisposable
        {
            public RE Execute(Func<RQ, RE> handler, RQ request, out bool result, out Exception exception)
            {
                try
                {
                    var risp = handler(request);
                    exception = null;
                    result = true;
                    return risp;
                }
                catch (Exception ex)
                {
                    exception = ex;
                    result = false;
                    return default(RE);
                }
            }

            //public void Execute<T>(Action<T> inneraction, T arg)
            //    where T : struct
            //{
            //    if (typeof(T).GetCustomAttributes(typeof(SerializableAttribute), true).Count() != 1)
            //        throw new ArgumentException("Generic T must support SerializableAttribute attribute!");

            //    inneraction(arg);
            //}

            public void Dispose()
            {
            }
        }

        public virtual void Dispose()
        {
            AppDomain.Unload(Domain);
        }
    }
}
